package com.klondike.common.xml;

import static com.klondike.common.io.IOUtils.asReader;
import static com.klondike.common.io.IOUtils.closeQuietly;

import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.WeakHashMap;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class XmlUtil {

    private static WeakHashMap<String, JAXBContext> jaxbContextCache = new WeakHashMap<String, JAXBContext>();

    public static String formatXml(String xml) throws IOException {
        Reader reader = asReader(xml);
        Writer writer = new StringWriter(xml.length() + 20);

        formatXml(new StreamSource(reader), new StreamResult(writer));

        closeQuietly(reader);
        closeQuietly(writer);

        return writer.toString();
    }

    public static void formatXml(StreamSource streamSource, StreamResult streamResult) throws IOException {
        formatXml(streamSource, streamResult, 2);
    }

    public static void formatXml(Source xmlSource, Result xmlResult, int indentAmount) throws IOException {
        try {
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            
            if (indentAmount > 0) {
                transformer
                        .setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indentAmount));
            }


            transformer.transform(xmlSource, xmlResult);

        } catch (TransformerException e) {
            throw new IOException(e);
        }
    }

    public static JAXBContext newJAXBContext(String ctxPath) throws JAXBException {
        JAXBContext jaxbContext = jaxbContextCache.get(ctxPath);
        if (jaxbContext == null) {
            jaxbContextCache.put(ctxPath, jaxbContext = JAXBContext.newInstance(ctxPath));
        }

        return jaxbContext;
    }

}
